Skip to content

feat(dsl,compiler): valley + cherry-valley horizon — surround generator, biome layer, tree layer (spec-0026 W-B, task #155) - #259

Open
stellarfeline wants to merge 17 commits into
worker/spec-0026-foundationfrom
worker/spec-0026-valley
Open

feat(dsl,compiler): valley + cherry-valley horizon — surround generator, biome layer, tree layer (spec-0026 W-B, task #155)#259
stellarfeline wants to merge 17 commits into
worker/spec-0026-foundationfrom
worker/spec-0026-valley

Conversation

@stellarfeline

@stellarfeline stellarfeline commented Aug 4, 2026

Copy link
Copy Markdown
Owner

spec-0026 slice W-B (task #155): the VALLEY + CHERRY-VALLEY surround generator, biome layer and tree layer, plugged into the W-A foundation's horizon contract. This is the engine work the hollow-vigil {base:"valley", flora:"cherry"} construction round needs; do not merge before the owner has viewed that round (player-experienceable — owner playtest is the gate; base branch = worker/spec-0026-foundation, which must land first anyway).

What this adds, at design level

Surround generator (crates/compiler/src/surround.rs, new). A valley is a mountain annulus of total footprint ratio× the scene's union XZ bounds (2..=3, default 2.5): flat walkable gap floor (top y=63 → valley walk_ref_y 64 — like flatland, a valley relocates nothing vs void), then a domain-warped, ridged-multifractal rim (rim_height default 48, ridge floor 0.3 so the ring never opens). Composed entirely from the in-house position-addressed value-noise family (edit.rs primitives made pub(crate); Musgrave ridge composition + Bridson Poisson disk are ideas-only re-implementations — ledger rows added to ACKNOWLEDGEMENTS). Everything derives from stream_seed(seed, "horizon/valley"); no trig anywhere (libm sin/cos is not bit-stable across hosts — the Poisson sampler uses square rejection instead).

Un-climbability is proven twice, promised never (spec §5).

  • By construction: every surround surface height is quantized to 2-block steps — a 1-block riser does not exist anywhere on the annulus, and vanilla players cannot step/jump 2. Trees are planted only where their whole canopy footprint clears the crest line (checked per cell, no guessed margin), so no canopy forms a ladder on the inner wall.
  • Empirically: DW0369 (build, exit 3) — a nav walk flood seeded on every gap-floor cell must never stand outward of the crest line, over the assembled (and, when a stage-7 script exists, edited) world. The generator also runs its own conservative flood (unlimited drops, tree solids included) and dies at generation if it ever regresses.

Biome layer. Per-band /fillbiome in setup_finish (after place_verify, so the chunks provably exist) — vanilla's own tint/ambience channel, no resource pack. flora: oakminecraft:windswept_forest, cherryminecraft:cherry_grove. The pass wraps in gamerule max_block_modifications <largest band volume> (1.21.11's renamed commandModificationBlockLimit) and restores the 32768 default; one command per band, so paint can never half-apply. Flatland note: flatland needs no fillbiome — its superflat generator already declares minecraft:plains; the fillbiome channel is surround-only.

Tree layer. Deterministic Bridson-family Poisson-disk scatter (r=7) over the crest band and outer face — 树在山上: never in the gap floor, never in the scene, never on the inner wall. One parameterized tree shape (the greenfield hand-shaped small tree grown a storey taller); species ids come from the flora table.

Cherry-valley is a parameter row, not a fork (criterion 6). One code path; flora/palette are positionally-parallel id tables (oak_log→cherry_log, oak_leaves→cherry_leaves, understory short_grass/fern → pink_petals/short_grass; rock ids shared). Proven at two levels: a generator test decodes same-seed oak/cherry tiles and asserts positions/states identical with ids mapped; process-level, tools/check-flora-parity.py (new) diffs two full emissions and fails on anything beyond flora/palette block ids + the biome id (manifest.json is exempt as a declaration mirror — it embeds the campaign declaration hash).

Plumbing (the W-A plug-in contract, followed to the letter). base_implemented(Valley)→true; valley walk_ref_y arm; DSL DW0141 reserved arm for valley/cherry-valley deleted (summit/sky stay reserved). Tiles are NOT an AreaPlacement: Plan.surround is a separate field, Plan::placed_pieces() is the explicit placement/model iterator — emit (structures, sentinels, forceload, place_all, place_verify), read_structures and the assembled voxel model opt in; boundary-region derivation, lighting scope, anchors and analysis keep reading plan.areas and never see the surround (spec §5 exclusion by construction). Render plan gains one vista shot (scene edge looking outward at the rim — the establishing shot of spec §6).

What CI now proves (all green locally)

  • v09_valley_builds_byte_identical_and_ships_surround: double-build byte-identity for the valley fixture (criterion 1 valley row); tiles shipped + placed; fillbiome + gamerule wrap present; vista shot present; void ambient in server.properties.
  • v09_cherry_valley_is_a_parameter_row: same-seed valley vs cherry-valley emission, check-flora-parity.py green (criterion 6).
  • surround::tests (10): determinism; ≤48×48×48 tile envelope + exact annulus coverage; even-step heights + ridge-never-opens; oak/cherry byte-parallelism at generator level; trees-stay-on-the-mountains; Poisson spacing; param-range refusal; build-range + no-gravity-block palette; nav flood over decoded serialized tiles; DW0369 red on a deliberately carved staircase (doctored world).
  • docs/reference/compiler.md: DW0369 catalog row + a valley behavior section, same PR; check-dw-codes.py fully green (191 codes documented + covered).
  • spec-0026 acceptance criterion 2 fixtures by code: DW0366 (valley ratio: 5 → validation exit 1), DW0367 (walk_y stripped → build exit 3), DW0364 (the feat(dsl,compiler): map editor L2 massing verbs — swap/insert/remove/rewire/reseed-piece (spec-0017 PR 3) #149 shape — a lying walk_y: 5 floods the interior with no waterline_y declared → build exit 3).

Visual parameters chosen (for the owner's 和谐 judgment — the vista shot + hollow-vigil round will show them)

  • gap floor width 12, inner slope run 18 (2-block terraced, bare rock gradient upward), rim_height default 48, ridge floor 0.3 (min crest ≈ 14 at the ring's lowest saddle).
  • tree spacing r=7 (≈ 360 trees around a 96×96 scene), crest band biased grassy so the tree line crowns the rim silhouette — for cherry, blossoms against the sky are exactly what the scene-edge vista frames.
  • Reference-scene footprint (96×96, ratio 2.5): 31 tiles, ~3.9 MiB gzipped structures, generation < 1 s release.

Known limits (recorded in compiler.md)

  • A scene too small for the ring to rise (annulus side < 30) legally yields a floor-only surround — no rim, no trees. The hello-world fixture is such a scene; the spec-0013 boundary clock (required for valley by DW0320) remains the enforcement, as for every horizon. The generator-level tests use full-size scenes.
  • Snapshot manifests do not list surround tiles as pieces (the voxel model contains them; the manifest lists authored areas). Flag if the reviewer-medium round wants them named.
  • check-world-settings.sh needed no valley case: valley ships the existing void superflat literal, already covered.

Stack sync + amendment (post-rebase onto the finished foundation, 370a45b)

  • Rebased onto the foundation's final tip; where both branches had independently fixed the same WIP debts (version bump test, datum tests — both converged on the same walk_y: 0 cliff fixture for the ocean-breach stranding test — DW catalog rows, fmt), the foundation's versions win and this branch's duplicates were dropped.
  • The foundation's reserved-bases test now pins both directions: summit/sky still DW0141; valley/cherry-valley landed and NOT reserved.
  • spec-0026 amendment (PR docs(specs): spec-0026 amendment — vista_radius definition + blend_width range #261) rides this PR (planner ruling; it already touches horizon_rules): summit.vista_radius is defined as measured outward from the scene bounding-box edge, floor 192 (shipped summit view-distance 12 × 16), default 208 (was 176 — the foundation had escalated the default/floor contradiction rather than resolve it by invention); flatland.blend_width range 1..=16 (0 is the forbidden hard material wall), default 6 unchanged. Both DW0366, with code-asserting range tests.

Machine red→green for the motivating scenario: worker/spec-0026-foundation alone refuses horizon:"valley" with DW0141 (red); with this branch the same campaign builds green with the surround shipped, and the doctored-staircase test is the DW0369 red→green pair.

🤖 Generated with Claude Code

@stellarfeline

Copy link
Copy Markdown
Owner Author

CI attribution (verified by reproducing each red at the foundation tip, 4599ad2, with this slice absent — all four fail there identically):

  • rust (fmt, clippy, test): (a) cargo fmt --check diffs in crates/dsl/src/lib.rs + stages.rs — unformatted foundation WIP (this branch deliberately does not format another slice's files); (b) cli::version_line asserts dsl 0.8.0 while the foundation bumped SUPPORTED_DSL_VERSION to 0.9.0.
  • prefab generators: tidal-keep-generator fmt — the foundation's walk_y emission commit.
  • docs (link check / DW gate): DW0364/DW0366/DW0367 catalog rows missing — the foundation's declared 'docs follow' debt. This slice's DW0369 row and behavior section are in.

Nothing in this slice's own surface is red: clippy -D warnings clean, the full workspace suite green locally except the inherited version_line, and the two new process gates (v09_valley double-build byte-identity, check-flora-parity.py) pass. This PR is stacked on worker/spec-0026-foundation and cannot merge before it; when the foundation lands its tests/docs commit, a rebase makes this PR's CI green with no changes here. Merge gate beyond CI: the owner's viewing of the hollow-vigil cherry-valley round (player-experienceable).

stellarfeline and others added 5 commits August 4, 2026 01:12
…isson tree layer, tile NBT synthesis (spec-0026 W-B)

Standalone core for the valley/cherry-valley horizon base, ahead of the
foundation branch's plug-in contract:

- crates/compiler/src/surround.rs: the mountain-annulus generator. Smoothed-
  rectangle radial profile with domain-warped distance, Musgrave-style ridged
  multifractal rim (in-house value-noise family, ideas-only composition),
  flat gap floor, even-step height quantization (no 1-block risers exist
  anywhere, so no standable staircase can form — by construction), plus a
  generator-side flood proof that the gap floor never reaches beyond the
  crest line (trees included as solids).
- Tree layer: deterministic Bridson-family Poisson-disk sampling (ideas-only,
  no trig — libm sin/cos is not bit-stable across hosts; ring candidates come
  from square rejection sampling), trees only outward of the crest line.
- Cherry-valley is a parameter row: flora/palette are parallel id tables on
  one code path; a unit test asserts a same-seed cherry emission maps back to
  the oak emission byte-parallel (positions/states identical, ids mapped).
- Tiles: ≤48×48 XZ, vertically sliced to ≤48, air-inclusive vanilla structure
  NBT, gzip mtime 0 (ADR-0006); per-band /fillbiome rects exported.
- edit.rs noise primitives (mix64/hash01/fade/value_noise) become pub(crate)
  so the surround module composes the one noise family instead of forking it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…tifractal (ideas-only, spec-0026 valley)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…f helper, vista camera

- Tree domain is now exact: a trunk is accepted only if every cell of its
  canopy footprint clears the crest line — no guessed slack racing the
  domain-warp gradient (the folklore margin red-flagged by its own test),
  and no canopy is ever sheared by a clip.
- Crest band biased grassier so the tree line crowns the rim silhouette the
  scene actually sees (for cherry-valley: 山上的樱花树 as the vista).
- ValleySurround::verify_unclimbable(&nav::World): the spec §5 empirical
  proof over the assembled world (flood from gap-floor starts; any standable
  cell beyond the crest line is the violation) — plus a unit test that runs
  it over the decoded serialized tiles.
- ValleySurround::vista_camera(): establishing-vista eye/look pair for the
  render plan (spec §6).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ugh plan/emit/assembled, biome paint, DW0369, vista (spec-0026 W-B)

Plug the valley surround generator into the W-A foundation contract:

- horizon.rs: base_implemented(valley)=true; valley walk_ref_y = 64 (gap
  floor top 63 — like flatland, a valley relocates nothing vs void).
- validate.rs: DW0141 reserved arm deleted for valley/cherry-valley
  (summit/sky stay reserved), per the contract's slice procedure.
- plan.rs: Plan.surround (SurroundPlan) — tiles generated at plan time from
  stream 'horizon/valley', deliberately NOT an AreaPlacement so
  boundary-region derivation, lighting scope, anchors and analysis never see
  them; Plan::placed_pieces() is the explicit placement/model iterator.
- emit.rs: tiles ship/forceload/place/verify through the existing bootstrap;
  per-band /fillbiome in setup_finish wrapped in a
  gamerule max_block_modifications raise/restore; DW0369 un-climbability
  proof (nav flood from the gap floor over the assembled — edited when a
  stage-7 script exists — world).
- assembled.rs/main.rs: surround tiles enter the voxel model and the
  structures map (generated bytes, never disk).
- render_plan.rs: one establishing 'vista' shot per surround build.
- tools/check-flora-parity.py + tests/v09_valley.rs: criterion 6
  (cherry-valley is a parameter row — same-seed emission diff confined to
  flora/palette block ids + biome id; manifest.json exempt as a declaration
  mirror) and criterion 1 valley row (double-build byte-identity), both
  process-level.
- docs/reference/compiler.md: DW0369 catalog row + valley behavior section.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… stack is green

The foundation commit (4599ad2) landed the horizon surface with 'tests/docs
follow'; PR CI red-flagged exactly that debt. Paid here, on the stacked
branch, so the pair is verifiable as a unit:

- fmt: crates/dsl/{lib,stages}.rs + prefabs/tidal-keep-generator (workspace
  and generator fmt --check both clean again).
- cli::version_line: asserts dsl 0.9.0 (spec-0026 raised it).
- docs/reference/compiler.md: DW0364 / DW0366 / DW0367 catalog rows;
  tools/check-dw-codes.py fully green (191 codes documented + covered).
- New coverage fixtures (spec-0026 acceptance criterion 2, by code):
  DW0366 (valley ratio 5 → validation exit 1), DW0367 (walk_y stripped from
  prefab metadata → build exit 3), DW0364 (the #149 shape: lying walk_y=5
  floods the interior with no waterline_y declared → build exit 3).
- cli::ocean_areas_sit_on_the_sea_level_datum: hello-room (walk_y 1) now
  correctly lands at 62 (walk plane 63, dry — the very placement the #149
  class got wrong); the island row (walk_y 3 → 60) needs a real island
  fixture and is noted as covered by the nobodys-cave content build — a
  doctored hello-room asserting walk_y 3 is a lie DW0364 rightly refuses.
- cli::ocean_waterline DW0344: the datum-consistent declaration is
  waterline_y = walk_y − 1 (hello-room: 0).
- edit::edit_ocean_breach_strands_the_player_dw0322: the datum fix retired
  the old scenario shape (a floor breach at 63 is the canonical escapable
  beach); rebuilt as a cliff piece (walk_y 0 → breach lip at 64, a wall to a
  swimmer) so the stranding proof is still exercised — and DW0364 stays
  green because nothing stands below the flood line.

No check weakened: every changed assertion now states the spec-0026 law
instead of the retired global-datum folklore.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
stellarfeline and others added 2 commits August 4, 2026 01:19
…ws, pin the valley un-reservation

The foundation's final commit (370a45b) landed its own DW0364/0366/0367
catalog rows and datum-test fixes (converging on the same walk_y-0 cliff
fixture this branch had built): its versions win, the duplicates this
branch carried are dropped. Its reserved-bases test still asserted valley
reserved; per the plug-in contract the arm's deletion belongs to this
slice, so the test now pins BOTH directions: summit/sky reserved (DW0141),
valley/cherry-valley landed and not reserved.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ult 208, blend_width 1..=16 (PR #261)

Planner design ruling, resolving the foundation's flagged contradiction
(default 176 < the spec's own view-distance floor):

- summit.vista_radius is now DEFINED as measured outward from the scene
  bounding-box edge; floor 192 (shipped summit view-distance 12 × 16, so
  peaks never pop out at the fog line); default raised to 208 (one chunk of
  margin). DW0366 below the floor.
- flatland.blend_width range 1..=16 (0 is a hard material wall — the exact
  no-pedestal outcome the interpenetration ruling forbids; past 16 the
  dither outgrows the seam). Default 6 unchanged. DW0366 outside it.
- HorizonSpec/horizon_defaults doc comments updated; DW0366 catalog row
  updated in the same PR; code-asserting cases added to the v09_horizon
  range test (DW-coverage gate).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@stellarfeline
stellarfeline force-pushed the worker/spec-0026-valley branch from 0be289d to b357ec3 Compare August 4, 2026 07:19
stellarfeline and others added 8 commits August 4, 2026 02:08
… obstacles, or light blockers (task #157 finding 1)

The hollow-vigil construction round red-flagged the class: assembled's
collision fallback modelled vanilla NO-COLLISION vegetation (grass tufts,
flowers, petals, saplings, crops, vines, ...) as full cubes.

Consequences, both directions:
- rejects-valid: a short_grass tuft on a valley terrace was a phantom
  standable cell splitting the 2-block riser into two climbable 1-block
  steps — DW0369 fired on a world that is not climbable in vanilla; the
  same phantom ladders made the annulus perimeter 'reachable' and DW0322
  fired on hundreds of outer-edge columns.
- accepts-invalid (worse): any walkability proof that ever stood a player
  ON a plant cell was unsound, and any flower cell measured light 0 as if
  opaque (false DW0210 the moment feet could legally occupy it).

Fix, at the class level (never the three ids one generator uses):
- assembled::is_no_collision_plant — the vanilla empty-collision-shape
  vegetation class; collision_top_16 returns 0 for it, so occupancy walks
  through it and stands on what is below. Lookalikes that DO collide
  (azalea, big_dripleaf, bamboo, cactus, pointed_dripstone, sea_pickle,
  leaves) deliberately stay conservative full cubes. Fidelity consequence:
  plant cells no longer dam the water-flood model (vanilla water flows
  into and breaks them).
- light::passes_light gains the same class (the occupancy-coupling
  invariant: nav-passable feet cells must not be opaque; the whole class
  is filterLight=0 in vanilla).
- surround: understory decor moves from the tile slicer into the
  GENERATION phase, and the generator's own un-climbability proof now runs
  over the finished tile contents (terrain + trees + decor,
  collision-classified via the one shared model) — the ordering gap that
  let the generator proof and DW0369 disagree is structurally closed.

Proofs:
- assembled unit tests pin the class (collision 0 + walked-through) and
  the colliding lookalikes; occupancy test: plants are neither floors nor
  obstacles.
- surround's nav-flood tests now decode every serialized tile and classify
  through the REAL occupancy model — no manual 'skip the tufts' list (the
  filter that masked this defect); the doctored-staircase DW0369 red case
  rides the same path.
- v09_open_scene_onto_gap_floor_builds_green: the #157 staging shape — a
  synthesized 48x48 scene fully open onto the gap floor — builds green
  (finding 2 resolved: DW0322 no longer fires; the un-climbable rim bounds
  the reachable set; no outer-containment design ruling needed).
- light unit test: the plant class passes light.

Existing-campaign note: repo fixtures are byte-identical (edit double-build
green); proofs only become MORE permissive (no phantom obstacles/floors/
darkness). One vanilla-fidelity behavior change: water no longer dams at
plant cells — plant-adjacent water features (tidal-keep shores, greenfield)
should be rebuilt in the next content round to confirm no fluid-model
verdict shifts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e vista (task #157 round 2)

Three follow-ups from the hollow-vigil ladder run, per the planner's design
ruling:

1. CLIFF-BAND DEGRADATION (ruling, implemented as specified). Hollow-vigil
   is 94x27: the short-axis band (20) cannot fit gap 12 + slope 18, so the
   rim never rose there and the perimeter ended as a walkable shelf over the
   void — DW0322 fired on 346 outer columns, and no legal ratio cleared it.
   The profile now derives per-column band width B = d/p (the exact side
   length on the faces, a smooth blend at corners): B >= 30 keeps the open
   valley; B < 30 keeps a >= 6-wide walkable gap floor and spends the rest
   on a near-vertical stepped cliff to full rim height — risers quantized
   >= 3 (stricter than the 2-block terraces), no outer decay, so the
   perimeter is unreachable cliff-top and DW0322 holds WITHOUT exemption.
   Quantization families (2 / 6 / 3) are separated by a 6-quantized seam
   strip so no 1-block riser can form at a family boundary; the warp
   amplitude shrinks with the gap so a 6-wide floor cannot be warp-pinched
   shut. Spec-0026 §5 amended in this PR (owner-review class — the planner
   surfaces the text change in chat); compiler.md updated.

2. INNER-SLOPE BLOSSOM. The vista framed zero cherry blocks: trees planted
   only past the crest. A second Poisson pass now stands trees on the inner
   slopes of non-degenerate bands, thinned sparse-near-floor to
   denser-upslope, every canopy LEANING two columns toward the valley (the
   greenfield lean idiom) so no leaf platform sits within hop-on/hop-off
   range of two upslope terraces; cliff bands carry the species as a
   sapling understory (no-collision class — zero proof impact). Tree
   positions stay flora-independent; oak_sapling<->cherry_sapling joins the
   parity map (script + tests). The un-climbability proofs (generator flood
   + DW0369) run unchanged over the new trees through the shared collision
   model.

3. VISTA CAMERA. Was 1.5 blocks off the scene edge staring at a wall. Now:
   eye at the campaign spawn cell (walkable by construction; scene-centre
   fallback) at player eye height, pitched up over the nearest scene edge at
   a point above the rim — the frame composes gap floor, blossomed slope,
   crest, sky. Deterministic from scene bbox + walk plane + per-side band
   params; fixed tie order.

Proofs (all green):
- degenerate_axis_degrades_to_an_unclimbable_cliff_band: 94x27 fixture —
  gap floor survives, cliff risers >= 3, full height at the tile edge, nav
  flood over the serialized tiles (decor + saplings included).
- no_one_block_riser_exists_and_ridge_never_opens: NO adjacent annulus
  columns differ by exactly 1, across all quantization-family seams.
- blossom_reaches_the_inner_slope_and_cliff_bands: cherry tree cells inward
  of the crest; sapling understory on the hollow-proportioned cliff bands.
- v09_hollow_proportioned_open_scene_builds_green: the ACTUAL degenerate
  aspect, fully open onto the gap floor — builds green, DW0322/DW0369
  silent (no containment invention needed).
- flora parity (unit + check-flora-parity.py) green with the sapling pair.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…wner ruling, 2026-08-04)

The owner overruled the cliff-band design with a simpler containment: the
surround band width never scales below what the rim physically needs. Per
axis, band = max((ratio-1)/2 x scene_axis, gap 12 + slope 18) - a hard
floor of 30 columns. The valley simply grows to contain any scene shape:
hollow-vigil's 94x27 short axis takes the floor while the long axis keeps
its proportional band (asymmetry accepted and expected); ONE rim profile
runs unchanged on every axis; no cliff geometry, no second proof path. All
cliff-band code from the previous commit is deleted, not kept as a dead
mode; the RadialParams machinery collapses back to the pinned constants.

One profile consequence: the outer decay now keys on the band remaining
PAST the crest (not raw annulus progress), so a band-floored axis ends
full-height at its tile edge - the perimeter is unreachable crest-top,
never a walkable shelf over the void, and DW0322 holds purely by
construction on the #157 open-carve staging shape.

Items 2 and 3 of the round ride along unchanged: inner-slope blossom
(valley-leaning canopies, density ramp) and the spawn-eye vista camera.
The cliff-only sapling understory is gone with the cliff bands (whole
trees fit everywhere now); the sapling parity pair is removed from
tools/check-flora-parity.py and the tests.

Proofs (all green):
- short_axis_band_floors_at_the_full_rim: 94x27 - long axis proportional,
  short axis floored at 30; gap floor survives; a real crest rises; nav
  flood over the serialized tiles.
- v09_hollow_proportioned_open_scene_builds_green: the ACTUAL degenerate
  aspect fully open onto the gap floor builds green (DW0322/DW0369 silent).
- a_carved_staircase_up_the_inner_slope_is_dw0369: the saboteur is now a
  carved notch (stone steps + air headroom) - the shape an edit batch
  could really cut - and is still caught.
- no-1-block-riser invariant, blossom_reaches_the_inner_slope, flora
  parity (script + unit), determinism, tile envelope: green.

Spec-0026 amendment text updated to the band-floor formula (owner-review
class - the planner surfaces it in chat); compiler.md updated in the same
PR. vista_radius/view-distance interplay untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The band floor (29e9432) replaced cliff-band degradation entirely, but three
comments and the vista expect string still described the abandoned design.
Render review would have hunted for a stepped cliff that cannot exist.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ene-rect moat fill (task #157 round 3)

1. SNAPSHOT PALETTE. block_color had no cherry entries, so 2044 cherry
   cells painted oak-green through the `_leaves` suffix family — the whole
   point of cherry-valley is that these read pink in every render. Explicit
   1.21.11 texture-average entries: cherry_leaves [229,158,187], cherry_log
   [92,58,58] (bark-dominant), pink_petals [224,168,190] (was falling to
   the magenta UNKNOWN fallback). Sweep: those three were the only gaps in
   the surround emission id set; a test pins the pinks against the suffix
   families and the whole emission palette off-fallback.

2. VISTA FOV (planner ruling). A spawn eye 40 blocks from a 48-high rim
   subtends ~52 degrees — no fixed FOV frames floor + crest + sky. The
   vista camera now aims at MID-rim on the nearest crest line and carries a
   per-shot vertical FOV derived from the geometry: frame bottom <= -15
   degrees elevation (gap floor in frame), top >= crest-top + 8 degrees of
   sky, clamped <= 110. Plumbing verified end to end: render_plan emits
   camera.fov (the existing pov_camera channel) and delve-render's Chunky
   scene emission honors it (scene.rs, default 70 when absent) — a new
   scene.rs test proves the field is never silently dropped, and a surround
   test asserts the 94x27 spawn-eye case frames floor AND crest-top
   arithmetically.

3. SCENE-RECT MOAT (planner design ruling). The #157 walls-down probe
   found DW0322 void exposure INSIDE the scene rect: the rect is the keep
   blob's bounding box, and columns outside every piece footprint were
   void. Scene-rect columns with ZERO piece-authored blocks now receive
   ambient gap-floor ground at floor_top_y with the gap floor's own surface
   dapple (world-coordinate noise - the pattern runs seamlessly from the
   annulus through the interior); columns with any authored block are
   untouched (pieces own their columns; authored overhangs/voids are
   intentional). Emitted as row-strip tiles (horizon/valley/m<n>) so no
   tile ever carries an air cell over a piece column; attached at the
   read_structures choke point (the authored-column set needs the piece
   bytes), so build, snapshot, blocking and the render plan all see the
   same completed surround; the un-climbability flood seeds on moat cells
   too. Fixtures with full plates emit zero moat tiles - existing
   emissions byte-identical.

Proofs (all green): snapshot palette test; scene.rs per-shot fov test;
vista_fov_frames_floor_and_crest_on_hollow_proportions;
the_moat_fills_only_unauthored_scene_columns (L-blob, authored columns
untouched, every unauthored column surfaced, byte-deterministic);
v09_blob_scene_with_void_moat_builds_green (walls-down irregular blob in a
94x27 rect - no DW0322 at the rect edge or interior). Spec-0026 amendment
text (short) + compiler.md same PR.

Note: the bot tier on this branch is stale-red only because the branch
predates merged #263; the batch integration branch carries the fix.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…round 4)

The scene-rect moat decided ownership of a column by asking "does a placed
piece author ANY block here", projecting authorship to 2D with no vertical
predicate. That reads a VERTICAL scene wrong: hollow-vigil's keep climbs, so
its later corridors, stairs and boss hall sit 4/8/12/16 blocks above the
gap-floor datum and author nothing at floor level — yet claimed their columns.
The moat skipped them and the gap-floor plane stayed void under the entire
elevated footprint (476 columns), with DW0322 firing along every seam where
filled ground met that void (159 columns, 165 violations on the walls-down
staging).

A column is now FLOORED only when a piece authors a block there at or below
the gap-floor top; floored columns stay untouched (the piece owns its ground,
holes and basements included, and the fill can never overwrite authored
blocks), while a column whose piece content is entirely above the gap floor is
filled — the valley floor runs on underneath it, exactly as under a column no
piece mentions. The rotation-aware transform is unchanged and matches both the
voxel model and vanilla's `place template … <rotation>` pivot.

Proof:
- new process-level regression `v09_elevated_storey_over_void_builds_green`:
  an open-air scene with a wing authored only on an upper storey. Red on the
  parent commit (DW0322, 126 columns at the wing edge), green with the fix.
- the same fixture path now WRITES a stage-7 batch, because boundary safety is
  only proven on the edit path — the three pre-existing open-scene tests
  asserted "no DW0322" over a build that never ran the check. They still pass,
  now for real.
- unit tests for `Plan::ground_columns`: footprint per rotation (3x5, all four),
  elevated storey floors nothing, overhang floors only the pillar, sub-datum
  cells still floor their column.
- hollow-vigil cherry-valley walls-down staging: 165 DW0322 errors -> exit 0.
  Double build byte-identical (ADR-0006), walls-up and walls-down.

docs/reference/compiler.md and spec-0026's continuity amendment restated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnrGdWS7ADsjCoJfiJc6GC
… kill tally (task #178) (#268)

* chore(harness): pick #263 (dsl_version allowlist covers 0.9.0) onto this base

worker/spec-0026-valley predates the fix, so the bot tier refuses every
0.9.0 campaign from this base and no ladder run is possible here. Identical
patch to the one already on main; it drops out on rebase/merge.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnrGdWS7ADsjCoJfiJc6GC

* fix(compiler,harness): the wave countdown is a liveness census, not a kill tally (task #178)

A `kill` objective asks for the wave to be DOWN. The emitted countdown asked
for something else — one player-credited kill per mob — and a mob has many
ways to die that credit nobody: burning in daylight, falling out of the world,
drowning, another mob. Every such death took a mob out of the world and left
the countdown above zero, asking the party to kill something that no longer
existed. No rest, no retry and no route recovers from that.

hollow-vigil shipped it (owner playtest 2026-08-05). Its opening beat's roof
and two walls come off for the open-air rule, the world is pinned at
`time set noon`, and the first wave walked out through the carved wall and
burned — `空洞步卒 burned to death`, reproduced live on the shipped datapack.
Countdown stuck at 3 with nothing alive, so `obj/purge` never completed,
`flag/halls-quiet` never set, `quest/the-barrow-key` never armed, and the
barrow key's chest was never placed anywhere in the world.

Design change, in two halves.

**Emission.** `tick` now reconciles the countdown against the world, once per
wave a `kill` objective adjudicates:

    execute if score #<w> dw.wave matches 1.. store result score #<w> dw.wave \
        if entity @e[tag=dw_wave_<w>]

Vanilla's own answer to "how many are left", so no new primitive and no
bookkeeping to keep honest. `matches 1..` does both guard jobs: before the
spawn the counter is unset, so an empty selector can never read as "cleared";
after a legitimate zero the line stops firing, so a finished wave stays
finished and a `respawns_on_rest` re-seat re-arms it cleanly. The
`player_killed_entity` decrement stays as a fast path — a mob the player just
killed is still matched by `@e` through its death animation.

**Harness.** `kill` was the one objective-bearing step that never called
`requireObjective`; it graded itself on mob-side evidence and returned. That
is why an all-green ladder shipped a softlock: `passed: true`, both encounters
census-cleared, and the delve considered the objective open forever. The
census still decides when to stop swinging; it no longer decides whether the
step is proven. `critical-path.json` format 2 already DOCUMENTED this contract
— `kill` simply did not implement it.

Ladder proof (same world both sides, `hollow-vigil` @ ce82515):
  * generated PackTest `kill_uncredited` — `verb_kill` minus the hand-fed
    `k_reward_<wave>` calls. Shipped datapack: 19/20, `Expected #party
    dw.o_warden to match 1, but got 0`. Fixed datapack: 20/20.
  * bot tier on the fixed build: `critical path 'hollow-vigil' PASSED`.
  * live rcon on both builds: countdown 3→3→3 (stuck, 0 alive, no chest) vs
    3→1→0 tracking the burn exactly.

Determinism: two builds byte-identical; the datapack diff against the staged
build is exactly the two census lines.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnrGdWS7ADsjCoJfiJc6GC

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
stellarfeline added a commit that referenced this pull request Aug 6, 2026
…am, not by this slice

The DW0366 row claimed `summit.vista_radius`'s floor was unenforceable because
the spec contradicted itself (floor 192 vs default 176). That was true when the
foundation slice reported it and stopped being true on 2026-08-04: the spec-0026
amendment (PR #261, on `main` for 66 commits now) resolved both open params —
`vista_radius` measured from the scene bounding box, floor 192, default 208; and
`blend_width` 1..=16.

Enforcement is deliberately NOT added here. Both params configure surrounds this
slice does not emit, and the valley slice (task #155 W-B, PR #259, based on this
branch) already carries the checks and the corrected default verbatim.
Implementing them twice would put a duplicate in the dependent branch's path for
no behaviour gained. The row now says what is true: the ambiguity is resolved,
the enforcement is owed, and which slice owes it.

No behaviour change: `dsl::validate::horizon_rules` and `horizon_defaults` are
byte-identical to this branch's pre-merge state.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AjQ5p1Kv5MrkGPumi7yXWL
…slice

Three conflicts, and the interesting damage was in what merged CLEAN.

main.rs (x2): the base added a `print_diags` of the placement stage's own
advisories to the view commands; this branch changed `read_structures` to
take `&mut Plan` (it calls `attach_valley_moat`). Both kept.

compiler.md (x2): the DW0141 reserved-surface row and DW0366's range row
had each moved on both sides. Took the base's rows (they carry everything
main added since this branch forked) and removed `valley`/`cherry-valley`
from the reserved list, since this is the PR that lands them.

scene.rs: NO conflict, and broken anyway. main renamed a scene file from
`<shot>.json` to `<campaign>_<shot>.json` (one stem per scene, so two
campaigns cannot share a cache key); this branch carries a test written
against the old name. Different regions of one file, so git merged the new
production code with the old assertion and reported success. Only running
it noticed. The test now derives its name from `scene_file_stem` instead
of spelling it, so the next rename cannot repeat this, and it names the
scenes it did find when it fails.
The worktree was created from the stale LOCAL branch rather than origin/,
so it lacked the wave-countdown census fix (#268) and the valley moat's
elevated-footprint fix (#157 round 4). Merged forward, never force-pushed:
a force-push here would have deleted both.

Doc conflicts (compiler.md x3, tools.md x1) were two branches editing the
same table anchor, each a different row: #268 rewrote `kill` (the countdown
is a liveness census, not a kill tally), main rewrote `collect` (v0.8
container adoption). Each row taken from the side that changed it — either
pick would silently revert real work. The third was disjoint prose about
one bot contract (the cast-ledger `pos`, and `kill` finally honouring the
objective); both kept.

And one more that git merged with NO conflict and broke anyway — the same
class as the scene.rs rename in the previous commit:

`DW0185` (i18n v2, spec-0029, from main) makes any authored player-visible
string emitted outside a text component an error, naming generated PackTest
sources as a permitted exclusion WHEN read through `dsl::l10n::plain`.
#268's new `kill_uncredited` PackTest source, written before that rule
existed, interpolated `c.world.content.title` raw. Every sibling header
already used `artifact_title(c)`, which is exactly that lowering; this one
now does too. Four CLI tests were red, including the byte-identity gate.

The pattern worth carrying: textual mergeability says nothing about
semantic compatibility, and neither of these would have been caught by
reviewing either branch alone.
@stellarfeline

Copy link
Copy Markdown
Owner Author

Task #176 — measured, root-caused, fixed, and gated

The owner's verdict was that the cherry valley "reads as a quarry". Measured on the hello-room valley fixture rather than eyeballed:

clause columns
in the annulus 4920
surface is grass 3140 (64%)
whole 5×5 canopy clears the crest line 147 (3.0%)
both 147 — identical, so grass never constrained anything
crest Poisson returned 0
inner-slope Poisson returned 7
canopy coverage 10.1%

Seven trees in an entire valley, and the trees themselves were fine — 4–5 block trunks, ~44 leaves each. Never malformation, and never the density constant.

Root cause

Bridson grows from its active list by jumps in [r, 2r), so a run seeded from one point samples only what those jumps reach. Both tree domains break that twice over: the annulus is a ring (a frontier cannot wrap it) and eligibility is speckle (grass noise ∧ a keep hash), so the frontier dies within a few steps. The crest pass never started at all — its single lattice scan found no in-domain column across a 3% speckle, and the sampler returned before its first jump.

Seeding from every in-domain lattice column costs one domain evaluation per lattice cell, is exactly as deterministic (positional order, far_enough still enforces spacing, seeds consume no RNG), and preserves blue-noise character.

The defect degrades with scale, which is why review missed it. On the 96×96 test scene the single-seed sampler still produced 1050 trees / 42.9%. It collapses on a narrow, speckled annulus — a small campaign, which is what was played.

Density — measured, and a proposal, not a decision

With the sampler fixed, TREE_SPACING is the only knob. Sweep recorded on the constant itself:

radius trees canopy
7.0 (was) 10 14.8%
5.0 25 32.0%
4.0 (now) 47 50.1%
3.0 80 61.9%
2.5 100 67.4%

4.0 is half-covered, so the crest silhouette still reads and the grove still has gaps to walk through. Density is an aesthetic call and belongs to the owner — say a number and it changes. What bounds it mechanically is DW_VALLEY_CLIMB, green across the whole range (the nav flood over the emitted world proves per build that a denser canopy grows no standable staircase).

The regression gate took two attempts to bind

blossom_reaches_the_inner_slope was green throughout and could not have caught this: it asserts inner blossom exists, and seven trees exist. The new test binds quantity — and, written first against the 96×96 scene, it passed with the defect deliberately restored. Moved to a 24×24 scene, where the same code gives 25 trees / 7.5% against 63 / 16.6% with the fix, and the floor sits between the two measurements.


Also on this branch

  • The foundation + the flood verb (spec-0030) merged in: the foundation alone reds DW0364 on 26 standable cells at world y=62 — the ocean line — in already released island content. Island now builds green in both languages, DW0364/DW0394/DW0395 all 0.
  • Three clean-but-broken merges fixed, none of which git flagged: a doc comment spliced through write_cell; a scene-file rename (<shot>.json<campaign>_<shot>.json) merged against a test written for the old name; and DW0185 (i18n v2) versus fix(compiler,harness): the wave countdown is a liveness census, not a kill tally (task #178) #268's PackTest header interpolating world.title raw where every sibling used artifact_title.

Full workspace green: tests, clippy -D warnings, fmt, and every static checker.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant